import {
    db, UserModel, SettingModel, DomainModel, Handler, UserNotFoundError, param, PERM, PRIV, Types, query, requireSudo
} from 'hydrooj';

// 修复 `getListForRender` 函数，给前端传递更多内容
export const coll: Collection<Udoc> = db.collection('user');
export const collV: Collection<VUdoc> = db.collection('vuser');
export const collGroup: Collection<GDoc> = db.collection('user.group');
UserModel.getListForRender = async function (domainId: string, uids: number[], arg: string[] | boolean) {
    const fields = ['_id', 'uname', 'mail', 'avatar', 'school', 'studentId', 'verify_flag', 'verify_name'].concat(arg instanceof Array ? arg : []);
    const showDisplayName = arg instanceof Array ? fields.includes('displayName') : arg;
    const [udocs, vudocs, dudocs] = await Promise.all([
        UserModel.getMulti({ _id: { $in: uids } }, fields).toArray(),
        collV.find({ _id: { $in: uids } }).toArray(),
        DomainModel.getDomainUserMulti(domainId, uids).project({ uid: 1, ...(showDisplayName ? { displayName: 1 } : {}) }).toArray(),
    ]);
    const udict = {};
    for (const udoc of udocs) udict[udoc._id] = udoc;
    for (const udoc of vudocs) udict[udoc._id] = udoc;
    if (showDisplayName) for (const dudoc of dudocs) udict[dudoc.uid].displayName = dudoc.displayName;
    for (const uid of uids) udict[uid] ||= { ...UserModel.defaultUser };
    for (const key in udict) {
        udict[key].school ||= '';
        udict[key].studentId ||= '';
        udict[key].displayName ||= udict[key].uname;
        udict[key].avatar ||= `gravatar:${udict[key].mail}`;
    }
    return udict as BaseUserDict;
};

//实名设置
class RealnameSetHandler extends Handler {
    @query('uidOrName', Types.UidOrName, true)
    @query('name', Types.string, true)
    async get(domainId: string, uidOrName: string, name: string) {
        this.response.template = 'verify_set.html'; // 返回此页面
        this.response.body = { uidOrName, name };
    }
    @param('uidOrName', Types.UidOrName)
    @param('flag', Types.number, true)
    @param('name', Types.string, true)
    async post(domainId: string, uidOrName: string, flag: number, name: string) {
        // 检查输入
        flag = parseInt(flag);
        const udoc = await UserModel.getById(domainId, +uidOrName)
            || await UserModel.getByUname(domainId, uidOrName)
            || await UserModel.getByEmail(domainId, uidOrName);
        if (!udoc)
            throw new UserNotFoundError(uidOrName);
        // 构建实名代码并更新
        if ([0, 1, 2].includes(flag)) await UserModel.setById(udoc._id, { verify_flag: flag });
        if (name) await UserModel.setById(udoc._id, { verify_name: name.trim() });
        this.response.redirect = this.url('verify_set');
    }
}

//展示实名用户
class RealnameShowHandler extends Handler {
    @query('page', Types.PositiveInt, true)
    @query('groupName', Types.string, true)
    async get(domainId: string, page = 1, groupName: string) {

        let filter = { verify_flag: { $exists: true, $ne: "" } };

        const groups = await UserModel.listGroup(domainId);
        if (groupName) {
            const groupInfo = groups.find(g => g.name === groupName);
            if (groupInfo) {
                filter._id = { $in: groupInfo.uids };
            }
        }

        const [dudocs, upcount] = await this.paginate(
            UserModel.getMulti(filter).sort({ verify_flag: -1, _id: -1 }),
            page,
            'ranking'
        );
        const udict = await UserModel.getList(domainId, dudocs.map((x) => x._id));
        const udocs = dudocs.map((x) => udict[x._id]);
        this.response.template = 'verify_show.html'; // 返回此页面
        this.response.body = { udocs, upcount, page, groupName, groups };
    }
}

//导入实名用户
class RealnameImportHandler extends Handler {
    async get() {
        this.response.body.verifys = [];
        this.response.template = 'verify_import.html';
    }

    @param('verifys', Types.Content)
    @param('draft', Types.Boolean)
    async post(domainId: string, _verifys: string, draft: boolean) {
        const verifys = _verifys.split('\n');
        const udocs: { username: string, flag: number, name: string }[] = [];
        const messages = [];

        for (const i in verifys) {
            const u = verifys[i];
            if (!u.trim()) continue;
            let [username, flag, name] = u.split('\t').map((t) => t.trim());
            if (username && !flag && !name) {
                const data = u.split(',').map((t) => t.trim());
                [username, flag, name] = data;
            }

            if (!username) continue;
            flag = parseInt(flag);

            // 验证用户是否存在
            const user = await UserModel.getByUname(domainId, username);
            if (!user) {
                messages.push(`Line ${+i + 1}: User ${username} not found.`);
                continue;
            }

            udocs.push({
                username, flag, name
            });
        }
        messages.push(`${udocs.length} verify records found.`);

        if (!draft) {
            for (const udoc of udocs) {
                try {
                    const user = await UserModel.getByUname(domainId, udoc.username);
                    if (!user) continue;

                    if (![0, 1, 2].includes(udoc.flag) && udoc.name === '') {
                        await UserModel.setById(user._id, { verify_flag: '', verify_name: '' });
                    } else {
                        if ([0, 1, 2].includes(udoc.flag)) {
                            await UserModel.setById(user._id, { verify_flag: udoc.flag });
                        }
                        if (udoc.name) {
                            await UserModel.setById(user._id, { verify_name: udoc.name });
                        }
                    }
                } catch (e) {
                    messages.push(e.message);
                }
            }
        }
        this.response.body.verifys = udocs;
        this.response.body.messages = messages;
    }
}

//修改密码
class ResetPwdHandler extends Handler {
    @requireSudo
    @query('uidOrName', Types.UidOrName, true)
    async get(domainId: string, uidOrName: string) {
        this.response.template = 'verify_resetpwd.html'; // 返回此页面
        this.response.body = { uidOrName };
    }
    @param('uidOrName', Types.UidOrName)
    @param('resetpwd', Types.Password)
    async post(domainId: string, uidOrName: string, resetpwd: string) {
        // 检查输入
        const udoc = await UserModel.getById(domainId, +uidOrName)
            || await UserModel.getByUname(domainId, uidOrName)
            || await UserModel.getByEmail(domainId, uidOrName);
        if (!udoc)
            throw new UserNotFoundError(uidOrName);

        if ((udoc.hasPriv(PRIV.PRIV_SET_PERM) && udoc._id !== this.user._id) || udoc.hasPriv(PRIV.PRIV_EDIT_SYSTEM)) {
            this.checkPriv(PRIV.PRIV_ALL);
        }
        // 修改密码
        await UserModel.setPassword(udoc._id, resetpwd);
        this.response.redirect = this.url('verify_resetpwd');
    }
}

//快速认证
class RealnameQuickHandler extends Handler {
    @query('page', Types.PositiveInt, true)
    async get(domainId: string, page = 1) {
        const [dudocs, upcount] = await this.paginate(
            UserModel.getMulti({ "_id": { $gte: 2 }, $or: [ { "verify_name": { $exists: false } }, { "verify_name": "" } ] }).sort({ verify_flag: 1, _id: -1 }),
            page,
            'ranking'
        );
        const udict = await UserModel.getList(domainId, dudocs.map((x) => x._id));
        const udocs = dudocs.map((x) => udict[x._id]);

        this.response.template = 'verify_quick.html'; // 返回此页面
        this.response.body = { udocs, upcount, page };
    }
}

// 配置项及路由
export async function apply(ctx: Context) {
    ctx.inject(['setting'], (c) => {
        c.setting.AccountSetting(
            SettingModel.Setting('setting_info', 'verify_flag', 0, 'number', '身份标记', '0是未添加身份，1是学生，2是老师', 2),
            SettingModel.Setting('setting_info', 'verify_name', '', 'text', '姓名', null, 2)
        );
    });
    ctx.Route('verify_show', '/verify/show', RealnameShowHandler);
    ctx.Route('verify_import', '/verify/import', RealnameImportHandler, PRIV.PRIV_SET_PERM);
    ctx.Route('verify_set', '/verify/set', RealnameSetHandler, PRIV.PRIV_SET_PERM);
    ctx.Route('verify_resetpwd', '/verify/resetpwd', ResetPwdHandler, PRIV.PRIV_SET_PERM);
    ctx.Route('verify_quick', '/verify/quick', RealnameQuickHandler, PRIV.PRIV_SET_PERM);
    ctx.injectUI('UserDropdown', 'verify_show', { icon: 'feeling-lucky', displayName: '验证' });
}
